package grobit.torrent

import grobit.bencode.Bencoder

/**
 * The torrent metainfo - the contents of a .torrent file.
 */
class MetaInfo {
    String announce
    List announceList
    Date creationDate
    String comment
    String createdBy
    String encoding

    int pieceLength
    List pieces = []
    int isPrivate
    String directory
    List files = []

    Sha1Data infoHash
    BigInteger totalSize = 0

    String getTracker(String scheme) {
        String start = scheme + ':'
        if(announce.startsWith(start)) {
            return announce
        }
        for(String tracker in announceList) {
            if (tracker.startsWith(start)) {
                return tracker
            }
        }
        return null
    }

    static createFromMap(Map data) {
        def metaInfo = new MetaInfo()

        metaInfo.announce = data.get('announce')
        metaInfo.announceList = data.get('announce-list') ?: []
        metaInfo.creationDate = data.containsKey('creation date') ? new Date(1000 * data.get('creation date').longValue()) : null
        metaInfo.comment = data.get('comment')
        metaInfo.createdBy = data.get('created by')
        metaInfo.encoding = data.get('encoding')

        Map infoData = data.get('info')
        metaInfo.infoHash = Sha1Data.calculateSha1(Bencoder.encode(infoData))
        metaInfo.pieceLength = infoData.get('piece length')
        metaInfo.isPrivate = infoData.get('private') ?: 0

        String pData = infoData.get('pieces')
        assert (pData.length() % 20) == 0
        while(pData.length()) {
            metaInfo.pieces.add(new Sha1Data(pData.substring(0, 20).getBytes('ISO-8859-1')))
            pData = pData.substring(20)
        }

        if(infoData.containsKey('files')) {
            metaInfo.directory = infoData.get('name')
            infoData.get('files').each() {
                metaInfo.files += MetaInfoFile.createFromMap(it)
            }
        } else {
            metaInfo.directory = '.'
            metaInfo.files += new MetaInfoFile(path: infoData.get('name'), length: infoData.get('length'))
        }

        metaInfo.files.each { metaInfo.totalSize += it.length }

        return metaInfo
    }

    @Override
    public String toString() {
        return "MetaInfo{" +
                "announce='" + announce + '\'' +
                ", announceList='" + announceList + '\'' +
                ", creationDate=" + creationDate +
                ", comment='" + comment + '\'' +
                ", createdBy='" + createdBy + '\'' +
                ", encoding='" + encoding + '\'' +
                ", pieceLength=" + pieceLength +
                ", pieces=" + pieces +
                ", isPrivate=" + isPrivate +
                ", directory='" + directory + '\'' +
                ", files=" + files +
                ", infoHash='" + infoHash + '\'' +
                '}';
    }
}

class MetaInfoFile {
    BigInteger length
    String md5sum
    String path

    static createFromMap(Map data) {
        MetaInfoFile file = new MetaInfoFile()
        file.length = data.get('length')
        file.md5sum = data.get('md5sum')
        file.path = data.containsKey('name') ? data.name : data.get('path').join('/')
        return file
    }

    @Override
    public String toString() {
        return "MetaInfoFile{" +
                "length=" + length +
                ", md5sum='" + md5sum + '\'' +
                ", path='" + path + '\'' +
                '}';
    }
}